package server

import (
	"context"
	"fmt"
	"gitee.com/go-mid/booter/bootservice"
	"gitee.com/go-mid/infra/xconfig/xviper"
	_ "gitee.com/go-mid/infra/xconfig/xyaml"
	"gitee.com/go-mid/infra/xlog"
	stat "gitee.com/go-mid/infra/xstat/sys"
	xprom "gitee.com/go-mid/infra/xstat/xmetric/xprometheus"
	"gitee.com/go-mid/infra/xtime"
	"gitee.com/go-mid/infra/xtrace"
	"math/rand"
	"strings"
	"sync"
)

type ViperServer struct {
	BaseServer
	shutdownCallback []func()
	bootConf         BootConfig
	servConf         servConfig
	servicePower     *bootservice.ServicePower
}

func NewViperServer(conf BootConfig) *ViperServer {
	server := &ViperServer{
		bootConf: conf,
		BaseServer: BaseServer{
			sname:                  conf.Sname,
			muLocks:                sync.Mutex{},
			services:               make(map[string]bootservice.Service),
			servInfos:              make(map[string]*bootservice.ServInfo),
			preServiceDriverFuncs:  make([]func(ctx context.Context, server Server) error, 0),
			postServiceDriverFuncs: make([]func(ctx context.Context, server Server) error, 0),
		},
		shutdownCallback: make([]func(), 0),
	}

	return server
}

type BootConfig struct {
	Sname   string                  `json:"sname"`
	CfgSrc  xviper.ConfigSourceType `json:"cfgsrc"`
	Cfgtype xviper.ConfigType       `json:"cfgtype"`
	Cfgroot string                  `json:"cfgroot"`
	Addrs   []string                `json:"addrs"`
	IsTest  bool                    `json:"istest"`
}
type servConfig struct {
	LogDir        string          `yaml:"logDir" json:"logDir"`
	LogLevel      string          `yaml:"logLevel" json:"logLevel"`
	LogFormat     xlog.FormatType `yaml:"logFormat" json:"logFormat"`
	LogRetainDays int             `yaml:"logRetainDays" json:"logRetainDays"`
	LogCompress   bool            `yaml:"logCompress" json:"logCompress"`
	BackdoorPort  string          `yaml:"backdoorPort" json:"backdoorPort"`
}

//启动服务，初始化过程是有序的
//1. 初始化配置中心
//2. 初始化服务唯一标识sid
//3. 初始化日志框架
//4. 初始化trace组件
//5. 执行业务前置服务，一般是初始化函数initfunc（注意：初始化函数不能时间过长，否则启动时间过久；初始化函数失败，则启动失败）
//6. 初始化 业务注册的服务（服务先初始化init，再驱动启动，driver）
//7. 初始化业务后置的服务，一般是一些定时任务
//8. 初始化后门程序，用于监控进程的状态
//9. 初始化埋点组件
func (m *ViperServer) Startup(ctx context.Context) error {
	fun := "ViperServer.Startup -->"
	//初始化服务配置信息
	if err := m.initConf(ctx); err != nil {
		xlog.Panicf(ctx, "%s initConf err: %v", fun, err)
		return err
	}
	//初始化sid
	if err := m.initSid(ctx); err != nil {
		xlog.Panicf(ctx, "%s initSid err: %v", fun, err)
		return err
	}
	// 初始化日志
	m.initLog(ctx)
	defer xlog.AppLogSync()
	defer xlog.StatLogSync()
	// 初始化服务进程打点
	stat.Init("log", m.ServName(ctx), "")
	// NOTE: bootservice 在初始化 trace middleware 前需要保证 xtrace.GlobalTracer() 初始化完毕
	m.initTracer(ctx)
	// App层 服务启动 前置初始化
	if err := m.initPreServiceDriverFuncs(ctx); err != nil {
		xlog.Panicf(ctx, "%s initPreServiceDriverFuncs err: %v", fun, err)
		return err
	}
	//初始化一个service启动器
	m.initServicePower(ctx)
	//初始化service
	if err := m.initProcessor(ctx); err != nil {
		xlog.Panicf(ctx, "%s callInitFunc err: %v", fun, err)
		return err
	}
	// App层 服务启动 后置初始化hook
	if err := m.initPostServiceDriverFuncs(ctx); err != nil {
		xlog.Panicf(ctx, "%s initPostServiceDriverFuncs err: %v", fun, err)
		return err
	}
	// NOTE: initBackdoor会启动http服务，但由于health check的http请求不需要追踪，且它是判断服务启动与否的关键，所以initTracer可以放在它之后进行
	m.initBackdoor(ctx)
	//初始化埋点
	m.initMetric(ctx)

	xlog.Infof(ctx, "server start success, servInfo: [%s]", m.servInfos)
	return nil
}

func (m *ViperServer) Shutdown(ctx context.Context) error {
	fmt.Println("====shutdown====")
	return nil
}

func (m *ViperServer) AppendShutdownCallback(ctx context.Context, f func()) {
	m.muLocks.Lock()
	defer m.muLocks.Unlock()
	m.shutdownCallback = append(m.shutdownCallback, f)
}

func (m *ViperServer) IsLocalRunning(ctx context.Context) bool {
	return true
}

func (m *ViperServer) ServInfos(ctx context.Context) map[string]*bootservice.ServInfo {
	m.muLocks.Lock()
	defer m.muLocks.Unlock()
	return m.servInfos
}
func (m *ViperServer) appendServInfos(ctx context.Context, sInfos map[string]*bootservice.ServInfo) {
	m.muLocks.Lock()
	defer m.muLocks.Unlock()
	for k, v := range sInfos {
		m.servInfos[k] = v
	}
	return
}

func (m *ViperServer) AppendService(services map[string]bootservice.Service) {
	for k, v := range services {
		m.services[k] = v
	}
}
func (m *ViperServer) AppendPreServiceDriverFuncs(initfns []func(ctx context.Context, server Server) error) {
	m.preServiceDriverFuncs = append(m.preServiceDriverFuncs, initfns...)
}
func (m *ViperServer) AppendPostServiceDriverFuncs(initfns []func(ctx context.Context, server Server) error) {
	m.postServiceDriverFuncs = append(m.postServiceDriverFuncs, initfns...)
}

func (m *ViperServer) initConf(ctx context.Context) error {
	fun := "Server.initConf -->"
	st := xtime.NewTimeStat()
	xlog.Infof(ctx, "%s start ", fun)
	defer func() {
		xlog.Infof(ctx, "%s end, durationMs: %d", fun, st.Millisecond())
	}()
	configCenter := xviper.NewViperConfigCenterManager(xviper.ViperConfigCenterManagerConf{
		Path:       fmt.Sprintf("%s/%s", m.bootConf.Cfgroot, m.bootConf.Sname),
		Addrs:      m.bootConf.Addrs,
		SourceType: m.bootConf.CfgSrc,
		ConfigType: m.bootConf.Cfgtype,
	})
	m.configCenter = configCenter
	var servConf servConfig
	err := m.configCenter.Unmarshal(ctx, "serv", &servConf)
	if err != nil {
		xlog.Errorf(ctx, "unmarshal serv config err: %v", err)
		return err
	}
	m.servConf = servConf
	return err
}
func (m *ViperServer) initSid(ctx context.Context) error {
	fun := "Server.initSid -->"
	st := xtime.NewTimeStat()
	xlog.Infof(ctx, "%s start ", fun)
	defer func() {
		xlog.Infof(ctx, "%s end, durationMs: %d", fun, st.Millisecond())
	}()

	//生成唯一id，此处随机生成
	sid := rand.Int()
	m.servId = sid
	return nil
}
func (m *ViperServer) initLog(ctx context.Context) error {
	fun := "Server.initLog -->"
	st := xtime.NewTimeStat()
	xlog.Infof(ctx, "%s start ", fun)
	defer func() {
		xlog.Infof(ctx, "%s end, durationMs: %d", fun, st.Millisecond())
	}()

	logDir := m.servConf.LogDir
	logLevel := m.servConf.LogLevel
	logFormat := m.servConf.LogFormat
	logRetainDays := m.servConf.LogRetainDays
	logCompress := m.servConf.LogCompress
	var logpath string
	if logDir == "" {
		logDir = "/data/bootservice/wread0"
	}
	if logDir == "console" {
		logpath = ""
	} else {
		logpath = fmt.Sprintf("%s/%s", logDir, m.Copyname(ctx))
	}
	if logLevel == "" {
		logLevel = "info"
	}
	if logFormat == "" {
		logFormat = xlog.JSONFormatType
	}

	xlog.Infof(ctx, "%s init log dir:%s ", fun, logpath)
	extraHeaders := map[string]interface{}{
		"ip": m.ServIp(ctx),
	}
	xlog.InitAppLogV2(logpath, "serv.log", convertLevel(logLevel), logFormat, extraHeaders, logRetainDays, logCompress)
	xlog.InitStatLog(logpath, "stat.log", logRetainDays, logCompress)
	xlog.SetStatLogService(m.ServLocation(ctx))
	xlog.Infof(ctx, "%s end ", fun)
	return nil
}

func convertLevel(level string) xlog.Level {
	level = strings.ToLower(level)
	switch level {
	case "debug":
		return xlog.DebugLevel
	case "info":
		return xlog.InfoLevel
	case "warn":
		return xlog.WarnLevel
	case "error":
		return xlog.ErrorLevel
	case "fatal":
		return xlog.FatalLevel
	case "panic":
		return xlog.PanicLevel
	default:
		return xlog.InfoLevel
	}
}

func (m *ViperServer) initPreServiceDriverFuncs(ctx context.Context) error {
	fun := "ViperServer.initPreServiceDriverFuncs -->"
	st := xtime.NewTimeStat()
	xlog.Infof(ctx, "%s start ", fun)
	defer func() {
		xlog.Infof(ctx, "%s end, durationMs: %d", fun, st.Millisecond())
	}()
	for _, initLogicFunc := range m.preServiceDriverFuncs {
		if err := initLogicFunc(ctx, m); err != nil {
			return err
		}
	}
	return nil
}
func (m *ViperServer) initPostServiceDriverFuncs(ctx context.Context) error {
	fun := "ViperServer.initPostServiceDriverFuncs -->"
	st := xtime.NewTimeStat()
	xlog.Infof(ctx, "%s start ", fun)
	defer func() {
		xlog.Infof(ctx, "%s end, durationMs: %d", fun, st.Millisecond())
	}()
	for _, initLogicFunc := range m.postServiceDriverFuncs {
		if err := initLogicFunc(ctx, m); err != nil {
			return err
		}
	}
	return nil
}

func (m *ViperServer) initTracer(ctx context.Context) error {
	fun := "ViperServer.initTracer -->"
	st := xtime.NewTimeStat()
	xlog.Infof(ctx, "%s start ", fun)
	defer func() {
		xlog.Infof(ctx, "%s end, durationMs: %d", fun, st.Millisecond())
	}()

	err := xtrace.InitDefaultTracer(m.ServLocation(ctx))
	if err != nil {
		xlog.Errorf(ctx, "%s init tracer err: %v", fun, err)
	}

	return err
}
func (m *ViperServer) initServicePower(ctx context.Context) error {
	fun := "ViperServer.initServicePower -->"
	st := xtime.NewTimeStat()
	xlog.Infof(ctx, "%s start ", fun)
	defer func() {
		xlog.Infof(ctx, "%s end, durationMs: %d", fun, st.Millisecond())
	}()

	m.servicePower = bootservice.NewServicePower(m.ServName(ctx))

	return nil
}

func (m *ViperServer) initProcessor(ctx context.Context) error {
	fun := "ViperServer.initProcessor -->"
	st := xtime.NewTimeStat()
	xlog.Infof(ctx, "%s start ", fun)
	defer func() {
		xlog.Infof(ctx, "%s end, durationMs: %d", fun, st.Millisecond())
	}()
	//测试不用启动服务
	if m.bootConf.IsTest {
		return nil
	}

	for n, p := range m.services {
		if len(n) == 0 {
			xlog.Errorf(ctx, "%s bootservice name empty", fun)
			return fmt.Errorf("bootservice name empty")
		}

		if n[0] == '_' {
			xlog.Errorf(ctx, "%s bootservice name can not prefix '_'", fun)
			return fmt.Errorf("bootservice name can not prefix '_'")
		}

		if p == nil {
			xlog.Errorf(ctx, "%s bootservice:%s is nil", fun, n)
			return fmt.Errorf("bootservice:%s is nil", n)
		} else {
			err := p.Init()
			if err != nil {
				xlog.Errorf(ctx, "%s bootservice: %s init err: %v", fun, n, err)
				return fmt.Errorf("bootservice:%s init err:%s", n, err)
			}
		}
	}

	_, err := m.loadDriver(ctx, m.services)
	if err != nil {
		xlog.Errorf(ctx, "%s load driver err: %v", fun, err)
		return err
	}
	//本地启动不需要注册

	return nil
}
func (m *ViperServer) initBackdoor(ctx context.Context) error {
	fun := "ViperServer.initBackdoor -->"
	st := xtime.NewTimeStat()
	xlog.Infof(ctx, "%s start ", fun)
	defer func() {
		xlog.Infof(ctx, "%s end, durationMs: %d", fun, st.Millisecond())
	}()

	//测试不用启动服务
	if m.bootConf.IsTest {
		return nil
	}
	if m.servConf.BackdoorPort == "" {
		xlog.Infof(ctx, "%s no backdoorport ", fun)
		return nil
	}

	backdoor := &bootservice.BackDoorHttp{
		Port: m.servConf.BackdoorPort,
	}
	err := backdoor.Init()
	if err != nil {
		xlog.Errorf(ctx, "%s init backdoor err: %v", fun, err)
		return err
	}
	_, err = m.loadDriver(ctx, map[string]bootservice.Service{"_PROC_BACKDOOR": backdoor})
	if err == nil {
		if err != nil {
			xlog.Errorf(ctx, "%s register backdoor err: %v", fun, err)
		}

	} else {
		xlog.Warnf(ctx, "%s load backdoor driver err: %v", fun, err)
	}

	return err
}

func (m *ViperServer) initMetric(ctx context.Context) error {
	fun := "ViperServer.initMetric -->"
	st := xtime.NewTimeStat()
	xlog.Infof(ctx, "%s start ", fun)
	defer func() {
		xlog.Infof(ctx, "%s end, durationMs: %d", fun, st.Millisecond())
	}()
	//测试不用启动服务
	if m.bootConf.IsTest {
		return nil
	}

	metrics := xprom.NewMetricProcessor()
	err := metrics.Init()
	if err != nil {
		xlog.Warnf(ctx, "%s init metrics err: %v", fun, err)
	}

	_, err = m.loadDriver(ctx, map[string]bootservice.Service{"_PROC_METRICS": metrics})
	return nil
}

func (m *ViperServer) loadDriver(ctx context.Context, procs map[string]bootservice.Service) (map[string]*bootservice.ServInfo, error) {
	fun := "Server.loadDriver -->"

	infos := make(map[string]*bootservice.ServInfo)

	for name, processor := range procs {
		servInfo, err := m.servicePower.PowerProcessorDriver(ctx, name, processor)
		if err == bootservice.ErrNilDriver {
			xlog.Infof(ctx, "%s bootservice: %s no driver, skip", fun, name)
			continue
		}
		if err != nil {
			xlog.Errorf(ctx, "%s load error, bootservice: %s, err: %v", fun, name, err)
			return nil, err
		}

		infos[name] = servInfo
		xlog.Infof(ctx, "%s load ok, bootservice: %s, serv addr: %s", fun, name, servInfo.Addr)
	}
	m.appendServInfos(ctx, infos)

	return infos, nil
}
